this.source = source;
this.urgent = false;
this.resident = false;
+ // 'transient' is a reserved keyword in JS, so we have to use an alternate variable name
+ this.isTransient = false;
this.expanded = false;
this._useActionIcons = false;
this._customContent = false;
this.resident = resident;
},
+ setTransient: function(isTransient) {
+ this.isTransient = isTransient;
+ },
+
setUseActionIcons: function(useIcons) {
this._useActionIcons = useIcons;
},
this.title = title;
this._iconBin = new St.Bin({ width: this.ICON_SIZE,
height: this.ICON_SIZE });
+ this.isTransient = false;
+ },
+
+ setTransient: function(isTransient) {
+ this.isTransient = isTransient;
},
// Called to create a new icon actor (of size this.ICON_SIZE).
}
this._summaryItems.push(summaryItem);
- this._newSummaryItems.push(summaryItem);
+
+ // We keep this._newSummaryItems to track any new sources that were added to the
+ // summary and show the summary with them to the user for a short period of time
+ // after notifications are done showing. However, we don't want that to happen for
+ // transient sources, which are removed after the notification is shown, but are
+ // not removed fast enough because of the callbacks to avoid the summary popping up.
+ // So we just don't add transient sources to this._newSummaryItems .
+ if (!source.isTransient)
+ this._newSummaryItems.push(summaryItem);
source.connect('notify', Lang.bind(this, this._onNotify));
this._notification.collapseCompleted();
this._notification.disconnect(this._notificationClickedId);
this._notificationClickedId = 0;
+ let notification = this._notification;
this._notification = null;
+ if (notification.isTransient)
+ notification.destroy();
},
_expandNotification: function(autoExpanding) {
this._summaryNotificationClickedId = 0;
let summaryNotification = this._summaryNotification;
this._summaryNotification = null;
+ if (summaryNotification.isTransient && !this._reNotifyWithSummaryNotificationAfterHide)
+ summaryNotification.destroy();
if (this._reNotifyWithSummaryNotificationAfterHide) {
this._onNotify(summaryNotification.source, summaryNotification);
this._reNotifyWithSummaryNotificationAfterHide = false;
}
},
- _newSource: function(title, pid) {
+ // Returns the source associated with ndata.notification if it is set.
+ // Otherwise, returns the source associated with the pid if one is
+ // stored in this._sources and the notification is not transient.
+ // Otherwise, creates a new source as long as pid is provided.
+ //
+ // Either a pid or ndata.notification is needed to retrieve or
+ // create a source.
+ _getSource: function(title, pid, ndata) {
+ if (!pid && !(ndata && ndata.notification))
+ return null;
+
+ // We use notification's source for the notifications we still have
+ // around that are getting replaced because we don't keep sources
+ // for transient notifications in this._sources, but we still want
+ // the notification associated with them to get replaced correctly.
+ if (ndata && ndata.notification)
+ return ndata.notification.source;
+
+ let isForTransientNotification = (ndata && ndata.hints['transient'] == true);
+
+ // We don't want to override a persistent notification
+ // with a transient one from the same sender, so we
+ // always create a new source object for new transient notifications
+ // and never add it to this._sources .
+ if (!isForTransientNotification && this._sources[pid])
+ return this._sources[pid];
+
let source = new Source(title, pid);
- this._sources[pid] = source;
+ source.setTransient(isForTransientNotification);
- source.connect('destroy', Lang.bind(this,
- function() {
- delete this._sources[pid];
- }));
+ if (!isForTransientNotification) {
+ this._sources[pid] = source;
+ source.connect('destroy', Lang.bind(this,
+ function() {
+ delete this._sources[pid];
+ }));
+ }
Main.messageTray.add(source);
return source;
let sender = DBus.getCurrentMessageContext().sender;
let pid = this._senderToPid[sender];
- let source = pid ? this._sources[pid] : null;
+
+ let source = this._getSource(appName, pid, ndata);
if (source) {
this._notifyForSource(source, ndata);
if (!ndata)
return;
- this._senderToPid[sender] = pid;
- source = this._sources[pid];
-
- if (!source)
- source = this._newSource(appName, pid);
- source.connect('destroy', Lang.bind(this,
- function() {
- delete this._senderToPid[sender];
- }));
-
+ source = this._getSource(appName, pid, ndata);
+
+ // We only store sender-pid entries for persistent sources.
+ // Removing the entries once the source is destroyed
+ // would result in the entries associated with transient
+ // sources removed once the notification is shown anyway.
+ // However, keeping these pairs would mean that we would
+ // possibly remove an entry associated with a persistent
+ // source when a transient source for the same sender is
+ // distroyed.
+ if (!source.isTransient) {
+ this._senderToPid[sender] = pid;
+ source.connect('destroy', Lang.bind(this,
+ function() {
+ delete this._senderToPid[sender];
+ }));
+ }
this._notifyForSource(source, ndata);
}));
notification.setUrgent(hints.urgency == Urgency.CRITICAL);
notification.setResident(hints.resident == true);
+ // 'transient' is a reserved keyword in JS, so we have to retrieve the value
+ // of the 'transient' hint with hints['transient'] rather than hints.transient
+ notification.setTransient(hints['transient'] == true);
let sourceIconActor = source.useNotificationIcon ? this._iconForNotificationData(icon, hints, source.ICON_SIZE) : null;
source.notify(notification, sourceIconActor);
},
_onTrayIconAdded: function(o, icon) {
- let source = this._sources[icon.pid];
- if (!source)
- source = this._newSource(icon.title || icon.wm_class || _("Unknown"), icon.pid);
+ let source = this._getSource(icon.title || icon.wm_class || _("Unknown"), icon.pid, null);
source.setTrayIcon(icon);
},